package utp.tepc.usina;

import java.util.ArrayList;
import java.util.List;

/**
 * @author Jackson
 * @version 1.0
 * @created 09-jun-2011 20:30:25
 */

public class Reator implements Subject
{
    private TipoEstado estado;
    private Otimo otimoPressao;
    private Otimo otimoTempratura;
    private IEqualizacao equalizacao;
    private ISensor sensor;
    private float temperatura;
    private float pressao;
    private String identificacao;
    private int holdStill = 0; // turnos de equalização
    private ControleOcorrencia ctrl;

    private List<Observer> observers;

    public Reator(String id, OtimoPressao otPressao, OtimoTemperatura otTemperatura) throws Exception
    {
	if (id.trim() == null || id.trim().length() <= 0)
	    throw new Exception("Informe a identificação do reator");

	if (otPressao == null)
	    throw new Exception("Informe o ótimo de pressão do Reator");

	if (otTemperatura == null)
	    throw new Exception("Informe o ótimo de temperatura do Reator");

	this.otimoPressao = otPressao;
	this.otimoTempratura = otTemperatura;
	this.identificacao = id;
	this.estado = TipoEstado.Normal;
	this.sensor = new GeradorRandomico();
	this.ctrl = ControleOcorrencia.getInstancia();
    }

    public void turno() throws Exception
    {
	if (this.estado == TipoEstado.Normal)
	    this.operar();
	else
	    this.equalizar();

	this.notifyObservers();
    }

    private void operar() throws Exception
    {
	temperatura = this.sensor.getTemperatura(this.otimoTempratura);
	pressao = this.sensor.getPressao(this.otimoPressao);

	int p = this.runPressao();
	int t = this.runTemperatura();

	this.verificarEstado(p, t);
    }

    private void verificarEstado(int p, int t) throws Exception
    {
	if (p > 0 || t > 0)
	{
	    this.estado = TipoEstado.Equalizando;

	    if (p > t)
	    {
		this.holdStill = p;
		
		this.equalizacao = new EqPLenta();
		TipoOcorrencia to = this.ctrl.getOcorrencia(this.equalizacao);
		
		if(to == TipoOcorrencia.Lenta)
		    this.equalizacao = new EqPLenta();
		else if(to == TipoOcorrencia.Normal)
		    this.equalizacao = new EqPModerada();
		else
		    this.equalizacao = new EqPIntensa();
	    }
	    else
	    {
		this.holdStill = t;
		
		this.equalizacao = new EqTSuave();
		TipoOcorrencia to = this.ctrl.getOcorrencia(this.equalizacao);
		
		if(to == TipoOcorrencia.Lenta)
		    this.equalizacao = new EqTSuave();
		else if(to == TipoOcorrencia.Normal)
		    this.equalizacao = new EqTMedio();
		else
		    this.equalizacao = new EqTAbrupto();
	    }
	}
    }
    
    private void equalizar()
    {
	this.holdStill--;

	if (this.holdStill > 0)
	    this.estado = TipoEstado.Equalizando;
	else
	    this.estado = TipoEstado.Normal;
    }

    private int runPressao()
    {
	float variacaoP;
	variacaoP = verificarPressao();

	if (variacaoP > 0)
	    this.estado = TipoEstado.Equalizando;

	if (variacaoP > 0 && variacaoP <= 1)
	{
	    return 2;
	}
	else if (variacaoP > 1)
	{
	    return 4;
	}
	else
	{
	    return 0;
	}
    }

    private int runTemperatura()
    {
	float variacaoT;
	variacaoT = verificarTemperatura();

	if (variacaoT > 0)
	    this.estado = TipoEstado.Equalizando;

	if (variacaoT > 0 && variacaoT <= 5)
	{
	    return 1;
	}
	else if (variacaoT > 5 && variacaoT <= 10)
	{
	    return 2;
	}
	else if (variacaoT > 10 && variacaoT <= 15)
	{
	    return 3;
	}
	else if (variacaoT > 15)
	{
	    return 4;
	}
	else
	{
	    return 0;
	}
    }

    private float verificarTemperatura()
    {
	float variacaoTemp = 0;
	if (temperatura > otimoTempratura.getMax())
	{
	    variacaoTemp = calculo(otimoTempratura.getMax(), temperatura);
	}
	else if (temperatura < otimoTempratura.getMin())
	{
	    variacaoTemp = calculo(otimoTempratura.getMin(), pressao);
	}
	return variacaoTemp;
    }

    private float verificarPressao()
    {
	float variacaoP = 0;
	if (pressao > otimoPressao.getMax())
	{
	    variacaoP = calculo(otimoPressao.getMax(), pressao);
	}
	else if (pressao < otimoPressao.getMin())
	{
	    variacaoP = calculo(otimoPressao.getMin(), pressao);
	}
	return variacaoP;
    }

    private float calculo(float valorRef, float valorVar)
    {
	float result = ((valorVar * 100) / valorRef) - 100;

	return result > 0 ? result : -1 * result;
    }

    @Override
    public void registerObserver(Observer obs)
    {
	if (this.observers == null)
	    this.observers = new ArrayList<Observer>();

	if (!this.observers.contains(obs))
	    this.observers.add(obs);
    }

    @Override
    public void removeObserver(Observer obs)
    {
	if (this.observers != null)
	    this.observers.remove(obs);

	if (this.observers.size() <= 0)
	    this.observers = null;
    }

    @Override
    public void notifyObservers()
    {
	ValObjInfoOporeacao info = new ValObjInfoOporeacao();
	info.idReator = this.identificacao;
	info.pressao = this.pressao;
	info.temperatura = this.temperatura;
	info.estado = this.estado;
	info.turnos = this.holdStill;
	info.equalizacao = this.equalizacao != null ? this.equalizacao.toString() : "";

	if (this.observers != null)
	    for (Observer o : this.observers)
		o.update(info);
    }
}